/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.gateway.route.builder;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;

import reactor.core.publisher.Flux;

import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * Used to build a {@link RouteLocator}.
 *
 * RouteLocatorBuilder 是 Spring Cloud Gateway 的核心组件之一，用于 编程式动态构建路由规则（Route）。
 * 它提供了一种灵活的方式，允许开发者在代码中直接定义路由，而不仅限于配置文件（如 application.yml）。
 *
 * 1. RouteLocatorBuilder 的作用
 * 		动态路由构建：通过 Java 代码定义路由规则（Route），适用于需要运行时动态调整路由的场景。
 * 		与 RouteDefinitionLocator 互补：
 * 				RouteDefinitionLocator：从配置（YAML、数据库等）加载路由定义（RouteDefinition）。
 * 		RouteLocatorBuilder：直接在代码中构建路由（Route），优先级更高（覆盖配置文件中的路由）。
 * 		支持断言（Predicate）和过滤器（Filter）：可以链式调用，灵活组合路由规则。
 *2. 核心方法与使用示例
 * 		(1) 基本路由构建
 * 			   @Bean
			 * public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
			 *     return builder.routes()
			 *         .route("user-service", r -> r
			 *             .path("/api/users/**")  // 路径匹配
			 *             .uri("lb://user-service")  // 目标服务（支持负载均衡）
			 *         )
			 *         .route("order-service", r -> r
			 *             .host("order.example.com")  // 域名匹配
			 *             .filters(f -> f
			 *                 .addRequestHeader("X-Request-ID", "123")  // 添加请求头
			 *                 .retry(3)  // 重试3次
			 *             )
			 *             .uri("lb://order-service")
			 *         )
			 *         .build();
			 * }
 * 		(2) 动态路由（结合业务逻辑）
 * 		       @Bean
			 * public RouteLocator dynamicRouteLocator(RouteLocatorBuilder builder, DynamicRouteService routeService) {
			 *     return builder.routes()
			 *         .route(r -> r
			 *             .path("/dynamic/**")
			 *             .filters(f -> f.rewritePath("/dynamic/(?<segment>.*)", "/${segment}"))
			 *             .uri(routeService.getTargetUri())  // 动态获取目标URI
			 *             .id("dynamic-route")
			 *         )
			 *         .build();
			 * }
 * 3. RouteLocatorBuilder 的底层原理
 * 		(1) 关键组件交互
 *		mermaid
 * 	sequenceDiagram
 *     participant Builder as RouteLocatorBuilder
 *     participant Route as Route
 *     participant Predicate as Predicate
 *     participant Filter as GatewayFilter
 *
 *     Builder->>Route: 创建Route.Builder
 *     Route->>Predicate: 添加Predicate（如Path、Host）
 *     Route->>Filter: 添加Filter（如AddRequestHeader）
 *     Builder->>Route: build() 生成最终Route对象
 *     Builder->>RouteLocator: 返回CompositeRouteLocator（组合所有路由）
 *
 *	  (2) 核心类解析
		类/接口	作用
		RouteLocatorBuilder	提供 routes() 方法，用于链式构建路由。
		Route.Builder	内部类，用于定义单个路由的 id、uri、predicates 和 filters。
		PredicateSpec	定义路由的匹配条件（如 path()、host()）。
		GatewayFilterSpec	定义路由的过滤器（如 addRequestHeader()、retry()）。
		RouteLocator	最终生成的路由定位器，供 FilteringWebHandler 使用。
 *
 *4. 与 RouteDefinitionLocator 的关系
 * 	RouteDefinitionLocator
 *		从外部配置（YAML、数据库等）加载 RouteDefinition（路由定义）。
 * 		适用于静态路由配置。
 *
 * RouteLocatorBuilder
 * 		通过代码动态构建 Route（路由对象）。
 * 		适用于动态路由（如从数据库、Redis 或 API 加载）。
 * 优先级：
 * 		RouteLocatorBuilder 定义的路由会 覆盖 RouteDefinitionLocator 中同 ID 的路由
 *
 *
 *
 */
public class RouteLocatorBuilder {

	private ConfigurableApplicationContext context;

	public RouteLocatorBuilder(ConfigurableApplicationContext context) {
		this.context = context;
	}

	/**
	 * Creates a new {@link Builder}.
	 * @return a new {@link Builder}.
	 */
	public Builder routes() {
		return new Builder(context);
	}

	/**
	 * A class that can be used to construct routes and return a {@link RouteLocator}.
	 */
	public static class Builder {

		private List<Buildable<Route>> routes = new ArrayList<>();

		private ConfigurableApplicationContext context;

		public Builder(ConfigurableApplicationContext context) {
			this.context = context;
		}

		/**
		 * Creates a new {@link Route}.
		 * @param id the unique id for the route
		 * @param fn a function which takes in a {@link PredicateSpec} and returns a
		 * {@link Route.AsyncBuilder}
		 * @return a {@link Builder}
		 */
		public Builder route(String id, Function<PredicateSpec, Buildable<Route>> fn) {
			Buildable<Route> routeBuilder = fn.apply(new RouteSpec(this).id(id));
			add(routeBuilder);
			return this;
		}

		/**
		 * Creates a new {@link Route}.
		 * @param fn a function which takes in a {@link PredicateSpec} and returns a
		 * {@link Route.AsyncBuilder}
		 * @return a {@link Builder}
		 */
		public Builder route(Function<PredicateSpec, Buildable<Route>> fn) {
			Buildable<Route> routeBuilder = fn.apply(new RouteSpec(this).randomId());
			add(routeBuilder);
			return this;
		}

		/**
		 * Builds and returns a {@link RouteLocator}.
		 * @return a {@link RouteLocator}
		 */
		public RouteLocator build() {
			return () -> Flux.fromIterable(this.routes).map(routeBuilder -> routeBuilder.build());
		}

		ConfigurableApplicationContext getContext() {
			return context;
		}

		void add(Buildable<Route> route) {
			routes.add(route);
		}

	}

	public static class RouteSpec {

		private final Route.AsyncBuilder routeBuilder = Route.async();

		private final Builder builder;

		RouteSpec(Builder builder) {
			this.builder = builder;
		}

		public PredicateSpec id(String id) {
			this.routeBuilder.id(id);
			return predicateBuilder();
		}

		public PredicateSpec randomId() {
			return id(UUID.randomUUID().toString());
		}

		private PredicateSpec predicateBuilder() {
			return new PredicateSpec(this.routeBuilder, this.builder);
		}

	}

}
